﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_OCF_Parser.h".
//-------------------------------------------------------------------

#include "ConfigTypes.h"

namespace UX_STL
{
namespace UXParser
{
	
//------------------------ CCfgBaseElement ------------------------//

	CCfgBaseElement::CCfgBaseElement(config_node_t pParent, e_config_object::type eType):
		_pNode(), _sFieldName(), _iRefCount(1), _eType(eType)
	{
		_pNode = pParent.PushBack( this );
	}


	CCfgBaseElement::~CCfgBaseElement()
	{
		_pNode.Elem() = null;
	}


	bool CCfgBaseElement::Free()
	{
		if ( (--_iRefCount) == 0 ) {
			//delete this;
			return true;
		}
		else
			return false;
	}
	

	bool CCfgBaseElement::Copy(config_const_node_t pFields)
	{
		s_RecursiveCopyFields( _pNode, pFields );
		return true;
	}
	

	bool CCfgBaseElement::CopyObj(config_const_node_t pFields)
	{
		FOR( i, pFields )
		{
			e_config_object::type	e_type = pFields[i].Elem()->GetType();

			if ( e_type == e_config_object::ENUM or
				 e_type == e_config_object::CONSTANT or
				 e_type == e_config_object::NAMESPACE or
				 e_type == e_config_object::USING )
			{
				continue;
			}

			s_RecursiveCopyFields( _pNode, pFields[i] );
		}
		return true;
	}


	void CCfgBaseElement::s_RecursiveCopyFields(config_node_t pNode, config_const_node_t pFields)
	{
		if ( !pFields.Valid() )
			return;

		pNode = pNode.PushBack( pFields.Elem() );
		pFields.Elem()->AddRef();

		FOR( i, pFields )
		{
			e_config_object::type	e_type = pFields[i].Elem()->GetType();

			if ( e_type == e_config_object::ENUM or
				 e_type == e_config_object::CONSTANT or
				 e_type == e_config_object::NAMESPACE or
				 e_type == e_config_object::USING  )
			{
				continue;
			}

			s_RecursiveCopyFields( pNode, pFields[i] );
		}
	}


		
//------------------------ CCfgNumberElem ----------------------//
	
	CCfgNumberElem::CCfgNumberElem(config_node_t pParent):
		CCfgBaseElement( pParent, e_config_object::VALUE ), _i64Value(0), _eValType(e_value::UNKNOWN)
	{}


	CCfgNumberElem::CCfgNumberElem(config_node_t pParent, uni_c_string sValue, usize &uErrorPos):
		CCfgBaseElement( pParent, e_config_object::VALUE ), _i64Value(0), _eValType(e_value::UNKNOWN)
	{
		float64_t	s_numb;
		bool		b_is_int	= true;
		
		uErrorPos = StringUtils::FromStrToNumber( sValue, s_numb, b_is_int );

		if ( uErrorPos != (usize)-1 )
			return;

		if ( b_is_int )
		{
			bool	b_signed = s_numb.GetI() < 0;

			if ( b_signed and s_numb.GetI() >= MinValue<int32>() ) {
				_iValue		= (int32)s_numb.GetI();
				_eValType	= e_value::INT32;
			}
			else
			if ( !b_signed and s_numb.GetI() <= MaxValue<uint32>() ) {
				_iValue		= (uint32)s_numb.GetI();
				_eValType	= e_value::INT32;
			}
			else {
				_i64Value	= s_numb.GetI();
				_eValType	= e_value::INT64;
			}
		}
		else
		{
			_dValue		= s_numb.GetF();
			_eValType	= e_value::DOUBLE;
		}
	}


	CCfgNumberElem::CCfgNumberElem(config_node_t pParent, CCfgBaseElement *pObj, bool bInc):
		CCfgBaseElement( pParent, e_config_object::VALUE ), _i64Value(0), _eValType(e_value::INT32)
	{
		_CopyValue( pObj, bInc );
	}
		

	CCfgNumberElem::CCfgNumberElem(const CCfgNumberElem &cObj, config_node_t pParent):
		CCfgBaseElement( pParent, e_config_object::VALUE ), _i64Value(cObj._i64Value), _eValType(cObj._eValType)
	{}


	CCfgNumberElem::~CCfgNumberElem()
	{}
	

	void CCfgNumberElem::SetValue(const void * pValue, e_value::type eValType)
	{
		MemCopy( _aBuf, pValue, sizeof(_aBuf) );
		_eValType = eValType;
	}


	void CCfgNumberElem::GetValue(string &str) const
	{
		switch( _eValType )
		{
			case e_value::BOOL	 :	str << _bValue;		break;
			case e_value::INT8	 :
			case e_value::INT16	 :
			case e_value::INT32	 :	str << _iValue;		break;
			case e_value::INT64	 :	str << _i64Value;	break;
			case e_value::FLOAT	 :	str << _fValue;		break;
			case e_value::DOUBLE :	str << _dValue;		break;
			default				 :	return;
		}
	}
	

	bool CCfgNumberElem::_CopyValue(CCfgBaseElement *pObj, bool bInc)
	{
		if ( pObj == null or pObj->GetType() != GetType() ) {
			WARNING( "" );
			return false;
		}

		CCfgNumberElem	*p_obj = (CCfgNumberElem*)pObj;

		_eValType	= p_obj->_eValType;
		MemCopy( _aBuf, p_obj->_aBuf, sizeof(_aBuf) );

		if ( bInc )
		{
			switch ( _eValType )
			{
				//case e_value::INT8	:
				//case e_value::INT16	:
				case e_value::INT32 :	++_iValue;		break;
				case e_value::INT64	:	++_i64Value;	break;
				case e_value::FLOAT	:	++_fValue;		break;
				case e_value::DOUBLE:	++_dValue;		break;
				default				:	WARNING( "can't inc not integer or float value" );
			}
		}

		return true;
	}
	
	
	int64 CCfgNumberElem::GetInt64() const
	{
		switch ( _eValType )
		{
			case e_value::BOOL		: return (int64)_bValue;
			case e_value::INT32		: return (int64)_iValue;
			case e_value::INT64		: return (int64)_i64Value;
			case e_value::FLOAT		: return (int64)_fValue;
			case e_value::DOUBLE	: return (int64)_dValue;
		}

		WARNING( "unknown value type" );
		return 0;
	}



//-------------------------- CCfgObjectElem -----------------------//
	
	CCfgObjectElem::CCfgObjectElem(config_node_t pParent, bool bInitList):
		CCfgBaseElement( pParent, e_config_object::OBJECT ), _bInitList(bInitList)
	{}

		
	CCfgObjectElem::CCfgObjectElem(config_node_t pParent, string &sType, uni_c_string_array_t &aNamespaces):
		CCfgBaseElement( pParent, e_config_object::OBJECT ), _bInitList(false)
	{
		_sTypeName.SetString( sType );
		_aNamespaces.SetMem( aNamespaces );
	}


	CCfgObjectElem::CCfgObjectElem(const CCfgObjectElem &cObj, config_node_t pParent):
		CCfgBaseElement( pParent, cObj.GetType() ), _sTypeName( cObj._sTypeName ),
		_aNamespaces( cObj._aNamespaces ), _bInitList( cObj._bInitList )
	{
		ASSERT( GetType() == e_config_object::OBJECT );
		CopyObj( cObj.GetNode() );
	}
		

	CCfgObjectElem::CCfgObjectElem(config_node_t pParent, string &sName, e_config_object::type eType):
		CCfgBaseElement( pParent, eType ), _bInitList(false)
	{
		SetName( sName );
		ASSERT( GetType() == e_config_object::NAMESPACE );
	}


	CCfgObjectElem::~CCfgObjectElem()
	{}
	

	void CCfgObjectElem::SetBindingName(uni_c_string sName)
	{
		ASSERT( not sName.Empty() );
		_sBindingName = sName;
	}
	


//-------------------------- CCfgArrayElem ------------------------//

	CCfgArrayElem::CCfgArrayElem(config_node_t pParent):
		CCfgBaseElement( pParent, e_config_object::ARRAY ), _sTypeName()
	{
	}

	CCfgArrayElem::~CCfgArrayElem()
	{
	}


//-------------------------- CCfgStringElem -----------------------//
	
	CCfgStringElem::CCfgStringElem(config_node_t pParent, string &sValue):
		CCfgBaseElement( pParent, e_config_object::STRING ), _sValue()
	{
		SetValue( sValue.ptr() );
		//_sValue.SetString( sValue );
	}
		

	CCfgStringElem::CCfgStringElem(const CCfgStringElem &cObj, config_node_t pParent):
		CCfgBaseElement( pParent, e_config_object::STRING ), _sValue(  cObj._sValue )
	{}


	CCfgStringElem::~CCfgStringElem()
	{}


	inline char CharToCode(char c)
	{
		if ( c >= '0' and c <= '9' )	return c - '0';
		if ( c >= 'a' and c <= 'f' )	return c - 'a' + char(10);
		if ( c >= 'A' and c <= 'F' )	return c - 'A' + char(10);
		
		WARNING( "incorrect char" );
		return 0;
	}
		

	bool CCfgStringElem::SetValue(uni_c_string sValue)
	{
		if ( !_sValue.Empty() )
			return false;

		for (uint i = 0; i < sValue.Length(); ++i)
		{
			if ( sValue[i] == '\\' )
			{
				switch ( sValue[++i] )
				{
					case '\\' :
						_sValue << '\\';
						break;

					case '"' :
						_sValue << '"';
						break;

					case 'n' :
						_sValue << NEWLINE;
						break;

					case 'x' :
						_sValue << char( (CharToCode( sValue[++i] ) << 1) + CharToCode( sValue[++i] ) );
						break;

					default :
						WARNING( "unknown symbol" );
				};
			}
			else
				_sValue << sValue[i];
		}
		
		return true;
	}


	
//----------------------------- CCfgUtils -------------------------//

	bool CCfgUtils::CreateType(CCfgBaseElement *pObj, config_node_t pParent, CCfgBaseElement *&pNewObj)
	{
		if ( pObj == null )
			return false;

		switch ( pObj->GetType() )
		{
			case e_config_object::VALUE	 :
				pNewObj = new CCfgNumberElem( *(CCfgNumberElem*)pObj, pParent );
				break;

			case e_config_object::STRING :
				pNewObj = new CCfgStringElem( *(CCfgStringElem*)pObj, pParent );
				break;

			case e_config_object::OBJECT :
				pNewObj = new CCfgObjectElem( *(CCfgObjectElem*)pObj, pParent );
				break;

			case e_config_object::ARRAY	 :
			case e_config_object::BINARY_ARRAY :
			case e_config_object::VARIANT_ARRAY :
				pNewObj = new CCfgBaseElement( pParent, pObj->GetType() );
				pNewObj->CopyObj( pObj->GetNode() );
				break;

			default	:
				return false;
		}

		return true;
	}


	bool CCfgUtils::Copy(config_const_node_t pFrom, config_node_t pToNode)
	{
		FOR( i, pFrom ) {
			_RecursiveCopy( pFrom[i], pToNode );
		}
		return true;
	}


	void CCfgUtils::_RecursiveCopy(config_const_node_t pFrom, config_node_t pToNode)
	{
		if ( !pFrom.Valid() )
			return;

		pToNode = pToNode.PushBack( pFrom.Elem() );
		pFrom.Elem()->AddRef();

		FOR( i, pFrom ) {
			_RecursiveCopy( pFrom[i], pToNode );
		}
	}

//-------------------------------------------------------------------

}	// UXParser
}	// UX_STL